package com.demoworld;

import org.junit.Test;

/**
 * Created by liwei.b on 2017/6/27.
 * volatile不是线程安全的，
 * 线程安全： 多个线程访问同一个 volatile变量， 不会出现不确定的结果， 每次结果都一致
 * 线程不安全， 反之。
 * 这里举例： 使用20个线程对volatile关键字修饰的n变量做递增操作，理论上最后结果应该是 20个线程 * 每个线程10000次递增 = 200,000,
 * 可是结果是吗？ 请运行testNotThreadSafe（），testNotThreadSafe1()两个方法进行校验。
 * <p>
 * 结果我们发现：
 * testNotThreadSafe（）方法每次都返回了不确定的结果， 并且都小于 我们理论计算的结果。
 * testNotThreadSafe1（）是我们想要的结果。
 * <p>
 * 分析：
 * testNotThreadSafe1（）在执行时increase1（）方法添加了同步锁， 保证每次只有一个线程可以执行这个方法。使我们想要的执行逻辑， 而volatile关键字并没有到达我们想要的预期
 * testNotThreadSafe（）方法没有达到我们的预期也在情理之中了， 但是为啥每次都小于200,000。解析class文件看到 #22位置， 在第一个 getstatic（）后， 其他线程可能已经改变了
 * 这个值， 但是这里拿到的还是旧的， 就新的值小， 所以加起来 每次的结果都比理论值小
 * <p>
 * public static void increase();
 * descriptor: ()V
 * flags: ACC_PUBLIC, ACC_STATIC
 * Code:
 * stack=2, locals=0, args_size=0
 * 0: getstatic     #22                 // Field n:I
 * 3: iconst_1
 * 4: iadd
 * 5: putstatic     #22                 // Field n:I
 * 8: return
 * LineNumberTable:
 * line 83: 0
 * line 84: 8
 */
public class VolatileNotThreadSafeTest {
    public static volatile int n = 0;

    public static void increase() {
        n++;
    }

    //这个测试用例，总共是20个线程， 每个限制都加加 10000 次， 最后的结果理论上是 20*10000=200,000
    //但是实际上， 每次的结果都小于理论值。
    @Test
    public void testNotThreadSafe() throws InterruptedException {
        Thread[] tmpThreads = new Thread[20];
        for (int i = 0; i <= tmpThreads.length - 1; i++) {
            tmpThreads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 10000; j++) {
                        increase();
                    }
                    System.out.println("Over.");
                }
            });
            tmpThreads[i].start();
        }
        Thread.sleep(2000);
        System.out.println(n);//最后的结果理论上是 20*10000=200,000,
    }

    public synchronized static void increase1() {
        n++;
    }

    //这个测试用例，总共是20个线程， 每个限制都加加 10000 次， 最后的结果理论上是 20*10000=200,000
    //但是实际上， 每次的结果都小于理论值。
    @Test
    public void testNotThreadSafe1() throws InterruptedException {
        Thread[] tmpThreads = new Thread[20];
        for (int i = 0; i <= tmpThreads.length - 1; i++) {
            tmpThreads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 10000; j++) {
                        increase1();
                    }
                    System.out.println("Over.");
                }
            });
            tmpThreads[i].start();
        }
        Thread.sleep(2000);
        System.out.println(n);//最后的结果理论上是 20*10000=200,000,
    }
}
